﻿using System;
using System.Net;
using System.Web;
using System.Linq;
using System.Globalization;
using Stimulus360.Models.Data;
using Stimulus360.Repositories;
using System.Collections.Generic;
using Microsoft.ServiceModel.Web;
using System.ServiceModel.Activation;
using Stimulus360.Services.Interfaces;
using Stimulus360.Models.Data.CustomTypes;
using Stimulus360.Repositories.Interfaces;

namespace Stimulus360.Services
{
    /// <summary>
    ///     This class is used to interact with the IDataServiceRepository to perform
    ///     to perform logical and CRUD operations on the Idea entity.
    /// </summary>
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class IdeaService : IIdeaService
    {
        #region IIdeaService Members

        /// <summary>
        ///     An instance of the IDataRepository
        /// </summary>
        private readonly IDataRepository _dataRepository;

        /// <summary>
        ///     Default Constructor. Creates and instance of the IDataRepository
        /// </summary>
        public IdeaService()
        {
            try
            {
                _dataRepository = new DataRepository();
            }
            catch (NullReferenceException exception)
            {
                // Exception conditions detected when invoking this service as a RESTFul service need to report an HTTP status code in the 
                //  400-500   range in addition to a message that details the exception
                throw new WebProtocolException(HttpStatusCode.BadGateway, "Could not connect to the database.Please check the connection and try again", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Overloaded constructor. Injects the given IDataRepository object to be used
        /// </summary>
        /// <param name="repository">
        ///     The IIdeaRepository to be used for Idea entity related operations.
        /// </param>
        public IdeaService(IDataRepository repository)
        {
            try
            {
                _dataRepository = repository;
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to get New Ideas 
        /// </summary>
        /// <param name="pageNo">
        ///     The Page number being displayed on the screen.
        /// </param>
        /// <param name="pageSize">
        ///     The number of ideas being shown in the screen.
        /// </param>
        /// <returns>
        ///     A Paginated List of New Ideas based on the page number and pagesize
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public PaginatedCollection<Idea> GetNew(string pageNo, string pageSize)
        {
            try
            {
                pageNo = HttpUtility.HtmlEncode(pageNo);
                pageSize = HttpUtility.HtmlEncode(pageSize);

                var ideaList = _dataRepository.GetNewIdeas();
                return new PaginatedCollection<Idea>(ideaList, pageNo.ToInt(), pageSize.ToInt());
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to get all New Ideas 
        /// </summary>
        /// <returns>
        ///     A List of all New Ideas
        /// </returns>
        public IEnumerable<Idea> GetNew()
        {
            try
            {
                return _dataRepository.GetNewIdeas().ToList();
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }

            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to get Idea details based on idea Id
        /// </summary>
        /// <param name="ideaId">
        ///     The idea Id for which Idea object is to be retrieved
        /// </param>
        /// <returns>
        ///     Idea object for the given IdeaId
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public Idea FindById(string ideaId)
        {
            try
            {
                ideaId = HttpUtility.HtmlEncode(ideaId);

                Idea idea = _dataRepository.FindIdeaById(ideaId.ToInt());
                if (idea != null)
                {
                    return idea;
                }
                throw new WebProtocolException(HttpStatusCode.NotFound, "No idea found for Idea ID " + ideaId, null);
            }
            catch (WebProtocolException)
            {
                throw;
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }

            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method returns projects related to the given idea
        /// </summary>
        /// <param name="ideaId">
        ///     Idea under consideration
        /// </param>
        /// <returns>
        ///     Projects related to the given idea.
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public ICollection<ProjectSummary> GetRelatedProjects(string ideaId)
        {
            try
            {

                ideaId = HttpUtility.HtmlEncode(ideaId);

                return _dataRepository.GetProjectsForIdea(ideaId.ToInt());
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }

            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to get a list of feedback for the given idea.
        /// </summary>
        /// <param name="ideaId">
        ///     IdeaID for the Idea for which the feedback is to be retrieved
        /// </param>
        /// <param name="pageNo">
        ///     The Page number being displayed on the screen.
        /// </param>
        /// <param name="pageSize">
        ///     The number of feedbacks being shown in the screen.
        /// </param>
        /// <returns>
        ///     A Paginated List of feedback based on the page number and pagesize
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public PaginatedCollection<IdeaRating> GetFeedback(string ideaId, string pageNo, string pageSize)
        {
            try
            {
                ideaId = HttpUtility.HtmlEncode(ideaId);
                pageNo = HttpUtility.HtmlEncode(pageNo);
                pageSize = HttpUtility.HtmlEncode(pageSize);

                var ratingList = _dataRepository.GetIdeaFeedback(ideaId.ToInt());
                return new PaginatedCollection<IdeaRating>(ratingList, pageNo.ToInt(), pageSize.ToInt());

            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are null", exception);
            }
           
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to add feedback for a given Idea
        /// </summary>
        /// <param name="feedback">
        ///     The feedback to be added
        /// </param>
        /// <remarks>
        ///     Input parameter type will need to change if method is exposed as REST.
        ///     also User Authentication will need to be implemented
        /// </remarks>
        public void AddFeedback(IdeaRating feedback)
        {
            try
            {
                if (feedback != null && feedback.IsValid)
                {
                    Idea ideaRated = _dataRepository.FindIdeaById(feedback.IdeaId);
                    if (ideaRated != null)
                    {
                        ideaRated.IdeaRatings.Add(feedback);
                        if (feedback.Favorite == true)
                        {
                            ideaRated.FavoritesCount = ideaRated.FavoritesCount.GetValueOrDefault(0) + 1;

                        }
                        double rating = ideaRated.AverageRating.GetValueOrDefault(0) *
                                        ideaRated.UserRatingsCount.GetValueOrDefault(0);

                        ideaRated.UserRatingsCount = ideaRated.UserRatingsCount.GetValueOrDefault(0) + 1;

                        ideaRated.AverageRating = (rating + feedback.Rating) / ideaRated.UserRatingsCount;
                        _dataRepository.UpdateIdea();
                    }
                    else
                    {
                        throw new WebProtocolException(HttpStatusCode.NotFound,
                                                       "No idea found for Idea ID " + feedback.IdeaId, null);

                    }

                }
                else
                {
                    throw new WebProtocolException(HttpStatusCode.BadRequest, "Feedback Provided was null/invalid", null);

                }
            }
            catch (WebProtocolException)
            {
                throw;
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "One or more objects was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception );
            }
        }

        /// <summary>
        ///     Method to get a list of ideas in the given category
        /// </summary>
        /// <param name="category">
        ///     The category title for the given category
        /// </param>
        /// <param name="pageNo">
        ///     The Page number being displayed on the screen.
        /// </param>
        /// <param name="pageSize">
        ///     The number of ideas being shown in the screen.
        /// </param>
        /// <returns>
        ///     A Paginated List of  Ideas for the given category 
        ///     based on the page number and pagesize
        /// </returns>   
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public PaginatedCollection<Idea> FindInCategory(string category, string pageNo, string pageSize)
        {
            try
            {
                category = HttpUtility.HtmlEncode(category);
                pageNo = HttpUtility.HtmlEncode(pageNo);
                pageSize = HttpUtility.HtmlEncode(pageSize);

                var categoryIdeaList = _dataRepository.FindIdeasInCategory(category);
                return new PaginatedCollection<Idea>(categoryIdeaList, pageNo.ToInt(), pageSize.ToInt());
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }

            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to find Ideas based on the given search criteria.
        /// </summary>
        /// <param name="searchCriteria">
        ///     String representing the type of search criteria: ByStatus,ByDate,ByRanking
        /// </param>
        /// <param name="query">
        ///     Search string to be deciphered based on the search criteria.
        /// </param>
        /// <param name="pageNo">
        ///     The Page number being displayed on the screen.
        /// </param>
        /// <param name="pageSize">
        ///     The number of ideas being shown in the screen.
        /// </param>
        /// <returns>
        ///     A Paginated List of  Ideas for the givens earch criteria
        ///     based on the page number and pagesize
        /// </returns>   
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public PaginatedCollection<Idea> Search(string searchCriteria, string query, string pageNo, string pageSize)
        {
            try
            {
                searchCriteria = HttpUtility.HtmlEncode(searchCriteria);
                query = HttpUtility.HtmlEncode(query);
                pageNo = HttpUtility.HtmlEncode(pageNo);
                pageSize = HttpUtility.HtmlEncode(pageSize);

                var criteria = (SearchCriteria)Enum.Parse(typeof(SearchCriteria), searchCriteria);
                IQueryable<Idea> ideaList = null;
                switch (criteria)
                {

                    case SearchCriteria.Status:
                        var status = (IdeaStatus)Enum.Parse(typeof(IdeaStatus), query);
                        ideaList = _dataRepository.FindIdeasByStatus(status);
                        break;
                    case SearchCriteria.Top:
                        int ranking = int.Parse(query, CultureInfo.CurrentCulture);
                        ideaList = _dataRepository.GetTopNIdeas(ranking);
                        break;
                    case SearchCriteria.SearchText:
                        ideaList = _dataRepository.FindIdeasBySearchString(query);
                        break;
                }

                return new PaginatedCollection<Idea>(ideaList, pageNo.ToInt(), pageSize.ToInt());

            }
            catch (ArgumentException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are invalid", exception);
            }
          
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are in invalid format",
                                               exception);
            }
           
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }

            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to add a new Idea to the system.
        /// </summary>
        /// <param name="idea">
        ///     Idea to be added.
        /// </param>
        /// <remarks>
        ///     Input parameter type will need to change if method is exposed as REST.
        ///     also User Authentication will need to be implemented
        /// </remarks>
        public void Add(Idea idea)
        {
            try
            {
                if (idea != null && idea.IsValid)
                {
                    _dataRepository.CreateIdea(idea);
                    idea.InvestmentCategory.IdeaCount = idea.InvestmentCategory.IdeaCount.GetValueOrDefault(0) + 1;
                    idea.InvestmentCategory.Score = idea.InvestmentCategory.Score.GetValueOrDefault(0) + 1;
                    _dataRepository.UpdateIdea();
                }
                else
                {
                    throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Provided was null/invalid", null);
                }

            }
            catch (WebProtocolException)
            {
                throw;
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "One or more objects were null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }

        }

        /// <summary>
        ///     This method is used to get the User's feedback for a specific Idea
        /// </summary>
        /// <param name="ideaId">
        ///     IdeaID for which feedback is to be retrieved.
        /// </param>
        /// <param name="userName">
        ///     User whose feedback is to be retrieved
        /// </param>
        /// <returns>
        ///     User Feedback for the given idea.
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public IdeaRating GetFeedback(string userName, string ideaId)
        {
            try
            {
                userName = HttpUtility.HtmlEncode(userName);

                ideaId = HttpUtility.HtmlEncode(ideaId);


                return _dataRepository.GetIdeaFeedback(ideaId.ToInt(), userName);
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "One or more arguments provided are in invalid format",
                                               exception);
            }

            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }

        }

        /// <summary>
        ///     Method to get list of ideas related to the given idea
        /// </summary>
        /// <param name="ideaId">
        ///     IdeaID for which related list is to be retrieved
        /// </param>
        /// <returns>
        ///     List of related ideas.
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public ICollection<Idea> GetRelated(string ideaId)
        {
            try
            {
                ideaId = HttpUtility.HtmlEncode(ideaId);
                Idea idea = _dataRepository.FindIdeaById(ideaId.ToInt());

                if (idea != null)
                {
                    return (from relatedIdea in _dataRepository.FindIdeasInCategory(idea.InvestmentCategory.Category)
                            where
                                relatedIdea.Status != (byte) IdeaStatus.Rejected && relatedIdea.IdeaId != ideaId.ToInt()
                            orderby relatedIdea.DateSubmitted descending
                            select relatedIdea).Take(10).ToList();
                }
                throw new WebProtocolException(HttpStatusCode.NotFound, "No idea found for Idea Id: " + ideaId,
                                               null);
            }
            catch (WebProtocolException)
            {
                throw;
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Id provided is in invalid format",exception);
            }
            catch (NullReferenceException exception)
            {
               
                    throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }

        }

        /// <summary>
        ///    Method used for RSS Feeds to get list of most popular ideas.
        /// </summary>
        /// <returns>
        ///     List of Most popular Ideas.
        /// </returns>
        /// <remarks>
        ///     All parameters have been taken in string format to enable REST calls in the future
        /// </remarks>
        public IEnumerable<Idea> GetTopN(string topN)
        {
            try
            {
                topN = HttpUtility.HtmlEncode(topN);

                return _dataRepository.GetTopNIdeas(topN.ToInt()).ToList() ;

            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Ranking  provided is null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Ranking provided is in invalid format", exception);
            }


            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }

        }

        /// <summary>
        ///     This method is used to get the Popular Ideas to be
        ///     synced with the CRM DB
        /// </summary>
        /// <returns>
        ///     List of popular Ideas
        /// </returns>
        public IEnumerable<Idea> GetPopular()
        {
            try
            {
                IQueryable<Idea> reviewedIdeas = _dataRepository.FindIdeasByStatus(IdeaStatus.Polling);
                var popularIdeas = from idea in reviewedIdeas
                                   where idea.UserRatingsCount >= 5 && idea.AverageRating >= 3.5
                                   select idea;
                return popularIdeas.ToList();

            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     This method is used to update the status of the given idea
        /// </summary>
        /// <param name="idea">
        ///     The idea to be updated
        /// </param>
        /// <param name="status">
        ///     The new status to be set
        /// </param>
        /// <remarks>
        ///     Input parameter type will need to change if method is exposed as REST
        /// </remarks>
        public void UpdateStatus(Idea idea, IdeaStatus status)
        {
            try
            {
                if (idea != null)
                {
                    Idea dbIdea = _dataRepository.FindIdeaById(idea.IdeaId);
                    if (dbIdea == null)
                    {
                        throw new WebProtocolException(HttpStatusCode.NotFound, "No Idea found for Idea Id: " + idea.IdeaId, null);
                    }
                    dbIdea.StatusLastUpdatedOn = DateTime.Now;
                    dbIdea.Status = (byte)status;
                    dbIdea.Comments = idea.Comments;
                    _dataRepository.UpdateIdea();
                }
                else
                {
                    throw new WebProtocolException(HttpStatusCode.BadRequest, "The Idea provided was null", null);
                }
            }

            catch (WebProtocolException)
            {
                throw;
            }
            catch (NullReferenceException exception)
            {

                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Database context was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }


        }

        /// <summary>
        ///     This method is called when an idea is accepted to form a project
        /// </summary>
        /// <param name="idea">
        ///     The Idea which has been accepted
        /// </param>
        /// <param name="projectId">
        ///     The asssociated project Id
        /// </param>
        /// <remarks>
        ///     Input parameter type will need to change if method is exposed as REST
        /// </remarks>
        public void Accept(Idea idea, string projectId)
        {
            try
            {
                if (idea != null)
                {
                    projectId = HttpUtility.HtmlEncode(projectId);

                    Idea acceptedIdea = _dataRepository.FindIdeaById(idea.IdeaId);
                    if (acceptedIdea != null)
                    {
                        acceptedIdea.Status = (byte)IdeaStatus.Accepted;
                        acceptedIdea.StatusLastUpdatedOn = DateTime.Now;
                        ProjectIdea projectIdea = new ProjectIdea
                                                      {
                                                          DateCreated =
                                                              acceptedIdea.StatusLastUpdatedOn.GetValueOrDefault(),
                                                          IdeaId = idea.IdeaId,
                                                          ProjectId = projectId.ToInt()
                                                      };
                        acceptedIdea.ProjectIdeas.Add(projectIdea);
                        _dataRepository.UpdateIdea();
                    }
                    else
                    {
                        throw new WebProtocolException(HttpStatusCode.NotFound,
                                                       "No Idea found for Idea Id: " + idea.IdeaId,
                                                       null);
                    }
                }
                else
                {
                    throw new WebProtocolException(HttpStatusCode.BadRequest, "Idea Provided was null", null);
                }
            }
            catch (WebProtocolException)
            {
                throw;
            }
            catch (ArgumentNullException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Project Id provided is null", exception);
            }
            catch (FormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest, "Project Id provided is in invalid format", exception);
            }

            catch (NullReferenceException exception)
            {

                throw new WebProtocolException(HttpStatusCode.InternalServerError , "The Database context was  null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }

        }

        #endregion



    }
}